﻿#region Using Statements
using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Xna.Framework;

#if WINDOWS_PHONE
using Microsoft.Devices.Sensors;
#endif

#endregion

#region COPYRIGHT

/*
    Copyright (c) 2007, 2008, 2009, 2010 
        Roland Rosenkranz (Glatzemann@email.de)
*/

#endregion

#region LICENSE

/*
    This file is part of starLiGHT.Input.

    starLiGHT.Input is free software: you can redistribute it and/or modify
    it under the terms of the GNU Lesser General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    starLiGHT.Input is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public License
    along with starLiGHT.Input.  If not, see <http://www.gnu.org/licenses/>.

    
    ADDITIONAL (commercial) LICENSES for starLiGHT.Input are available on request.
*/

#endregion

#region Version Stuff
// **************[ starLiGHT.Engine SVN ]**********************
// * $Rev:: 1652                  $:  Revision of last commit *
// * $Author:: glatzemann         $:  Author of last commit   *
// * $Date:: 2010-11-16 07:41:22 #$:  Date of last commit     *
// ************************************************************
#endregion

namespace starLiGHT.Input
{
#if WINDOWS_PHONE || WINDOWS
    public class MotionSensingDevice : IControlDevice
    {
#if WINDOWS_PHONE            
        Accelerometer sensor;
        DateTimeOffset lastSensorTime;
#endif

        ThreeAxisControl rawAcceleration;
        ThreeAxisControl smoothAcceleration;

        float smoothing = .1f;
        Vector3 sensorError = new Vector3(-0.09f, -0.02f, 0.04f);

        // If a real sensor is not available, we can fake up some inputs.
        // We will return a reading tilted FakeRollPhi away from horizontal,
        // in a direction given by FakeRollTheta, which wobbles at
        // FakeRollSpeed radians per second.
        private float FakeRollPhi = MathHelper.Pi / 8;
        private float FakeRollTheta = 0;
        private float FakeRollSpeed = 1.0f; // 1 radian/second, or one circle every ~6 seconds

        public MotionSensingDevice()
        {
            rawAcceleration = new ThreeAxisControl(new Vector3(0, 0, -1));
            smoothAcceleration = new ThreeAxisControl(new Vector3(0, 0, -1));
#if ZUNE || WINDOWS_PHONE
            sensor = new Accelerometer();
            sensor.ReadingChanged += OnReadingChanged;

            // try to start the sensor only on devices, catching the exception if it fails            
            if (Microsoft.Devices.Environment.DeviceType == Microsoft.Devices.DeviceType.Device)
            {
                try
                {
                    sensor.Start();
                }
                catch (AccelerometerFailedException)
                {
                    sensor = null;
                }
            }
            else
            {
                sensor = null;
            }
#endif

        }

        public bool IsConnected
        {
            get { return true; }
        }

        public DeviceType DeviceType
        {
            get { return DeviceType.MOTION_SENSING; }
        }

        public bool HasPlayerIndex
        {
            get { return false; }
        }

        public PlayerIndex PlayerIndex
        {
            get { return PlayerIndex.One; }
        }

        public void Update(GameTime gameTime)
        {
#if ZUNE || WINDOWS_PHONE
            if (sensor == null)
            {
                FakeRollTheta += (float)gameTime.ElapsedGameTime.TotalSeconds * FakeRollSpeed;
                FakeRollTheta = MathHelper.WrapAngle(FakeRollTheta);
                rawAcceleration.Update(new Vector3((float)Math.Sin(FakeRollPhi) * (float)Math.Cos(FakeRollTheta),
                                                   (float)Math.Sin(FakeRollPhi) * (float)Math.Sin(FakeRollTheta),
                                                 -(float)Math.Cos(FakeRollPhi)));
                smoothAcceleration.Update(rawAcceleration.Position);
            }
#endif
        }

#if WINDOWS_PHONE
        // Callback when new actual sensor readings are available.
        void OnReadingChanged(object sender, AccelerometerReadingEventArgs args)
        {
            rawAcceleration.Update(new Vector3((float)args.X, (float)args.Y, (float)args.Z) - sensorError);

            float dt = (float)args.Timestamp.Subtract(lastSensorTime).TotalSeconds;
            lastSensorTime = args.Timestamp;
            dt = MathHelper.Clamp(dt, 0.0f, 1.0f);

            float p = (float)Math.Exp(-dt / smoothing);
            smoothAcceleration.Update(Vector3.Lerp(rawAcceleration.Position, smoothAcceleration.Position, p));
        }
#endif

        public ThreeAxisControl RawAcceleration
        {
            get { return rawAcceleration; }
        }

        public ThreeAxisControl SmoothAcceleration
        {
            get { return smoothAcceleration; }
        }
    }
#endif
}
